package net.sesami.nhg.ebd.batch.service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import net.sesami.gtp.eprocure.xml.jaxb.AdditionalFields;
import net.sesami.gtp.eprocure.xml.jaxb.Address;
import net.sesami.gtp.eprocure.xml.jaxb.Attachment;
import net.sesami.gtp.eprocure.xml.jaxb.Attachments;
import net.sesami.gtp.eprocure.xml.jaxb.BillTo;
import net.sesami.gtp.eprocure.xml.jaxb.BuyerInformation;
import net.sesami.gtp.eprocure.xml.jaxb.Detail;
import net.sesami.gtp.eprocure.xml.jaxb.Document;
import net.sesami.gtp.eprocure.xml.jaxb.ExchangeRateDetail;
import net.sesami.gtp.eprocure.xml.jaxb.Header;
import net.sesami.gtp.eprocure.xml.jaxb.ItemDetail;
import net.sesami.gtp.eprocure.xml.jaxb.ObjectFactory;
import net.sesami.gtp.eprocure.xml.jaxb.OrderInformation;
import net.sesami.gtp.eprocure.xml.jaxb.PaymentAndDelivery;
import net.sesami.gtp.eprocure.xml.jaxb.ShipTo;
import net.sesami.gtp.eprocure.xml.jaxb.SigningAuthority;
import net.sesami.gtp.eprocure.xml.jaxb.Summary;
import net.sesami.gtp.eprocure.xml.jaxb.VendorInformation;
import net.sesami.nhg.ebd.batch.constants.IConstants;
import net.sesami.nhg.ebd.batch.dao.PODAO;
import net.sesami.nhg.ebd.batch.domain.hibernate.PO;
import net.sesami.nhg.ebd.batch.domain.hibernate.POAttachment;
import net.sesami.nhg.ebd.batch.domain.hibernate.POItem;
import net.sesami.nhg.ebd.batch.domain.hibernate.POItemAttachment;
import net.sesami.nhg.ebd.batch.email.ApplicationEmailer;
import net.sesami.nhg.ebd.batch.exception.ApplicationException;
import net.sesami.nhg.ebd.batch.properties.ApplicationProperties;
import net.sesami.nhg.ebd.batch.utils.DateTimeUtils;

import org.apache.log4j.Logger;

public class POService {
	private static final Logger LOGGER = Logger.getLogger(POService.class);
	private PODAO podao = new PODAO();
	private DateTimeUtils dateTimeUtils = new DateTimeUtils();
	private ApplicationProperties applicationProperties = new ApplicationProperties();
	private ApplicationEmailer applicationEmailer = new ApplicationEmailer();

	public void updatePOAsWaitingForSupplierResponse(Integer poid) {
		this.podao.updatePOAsWaitingForSupplierResponse(poid);
	}

	public void updateCanceledPOAsSentToSupplier(Integer poid) {
		this.podao.updateCanceledPOAsSentToSupplier(poid);
	}

	public void generatePOXMLs() {
		List<PO> pos = this.podao.getAllPOReadyToSend();
		for (PO po : pos) {
			this.generatePOXML(po);
		}
	}

	public void generatePOXML(PO po) {
		try {
			SchemaFactory schemaFactory = SchemaFactory
					.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			Schema schema = schemaFactory
					.newSchema(new File(
							this.applicationProperties
									.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_SCHEMA)));
			File writer = new File(this.getXMLFileName(po));
			JAXBContext jaxbContext = JAXBContext.newInstance(Document.class);
			javax.xml.bind.Marshaller jaxbMarshaller = jaxbContext
					.createMarshaller();
			jaxbMarshaller.setSchema(schema);
			jaxbMarshaller.setProperty(
					javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, true);
			jaxbMarshaller.marshal(this.createPOXMLDocument(po), writer);
			this.updatePOStatus(po);
		} catch (Exception e) {
			String message = String.format(
					"Exception occured while generating XML for PO: %d",
					po.getPOID());
			this.applicationEmailer.sendExceptionEmail(message, e);
		}
	}

	public void updatePOStatus(PO po) {
		if (po.getPOStatusID().intValue() == IConstants.POStatus.APPROVED
				.intValue()) {
			this.updatePOAsWaitingForSupplierResponse(po.getPOID());
		} else if (po.getPOStatusID().intValue() == IConstants.POStatus.CANCELLED_BY_BUYER
				.intValue()) {
			this.updateCanceledPOAsSentToSupplier(po.getPOID());
		} else {
			String message = String.format(
					"Exception occured while updating PO status for PO: %d",
					po.getPOID());
			throw new ApplicationException(message);
		}

	}

	private String getXMLFileName(PO po) {
		String outPutPath = this.applicationProperties
				.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_OUTBOUND);
		String fileName = po.getPOID() + "-"
				+ dateTimeUtils.getCurrentTimeInYYYYMMDDMMSS() + ".xml";

		return outPutPath + fileName;
	}

	public Document createPOXMLDocument(PO po) {
		ObjectFactory objectFactory = new ObjectFactory();
		Document document = objectFactory.createDocument();
		document.setDetail(this.createDetail(objectFactory, po));
		document.setHeader(this.createHeader(objectFactory, po));
		document.setSummary(this.createSummary(objectFactory, po));

		return document;
	}

	public Detail createDetail(ObjectFactory objectFactory, PO po) {
		Detail detail = objectFactory.createDetail();
		detail.getItemDetail()
				.addAll(this.createItemDetails(objectFactory, po));

		return detail;
	}

	public Header createHeader(ObjectFactory objectFactory, PO po) {
		Header header = objectFactory.createHeader();
		header.setAdditionalFields(this.createHeaderAdditionalFields(
				objectFactory, po));
		header.setBillTo(this.createBillTo(objectFactory, po));
		header.setBuyerInformation(this.createBuyerInformation(objectFactory,
				po));
		header.setDocRevisionNo(0);
		header.setDocumentID("");
		if (po.getPOStatusID().intValue() == IConstants.POStatus.APPROVED
				.intValue()) {
			header.setDocumentType(this.applicationProperties
					.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_DocumentType));
		} else if (po.getPOStatusID().intValue() == IConstants.POStatus.CANCELLED_BY_BUYER
				.intValue()) {
			header.setDocumentType(this.applicationProperties
					.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_DocumentType_CANCEL));
		} else {
			String message = String
					.format("Exception occured while generating PO XML for PO: %d. The status of PO is not either Approved or Cancelled by buyer",
							po.getPOID());
			throw new ApplicationException(message);
		}
		header.setHubID(this.applicationProperties
				.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_HubID));
		header.setOrderInformation(this.createOrderInformation(objectFactory,
				po));
		header.setPaymentAndDelivery(this.createPaymentAndDelivery(
				objectFactory, po));
		header.setReleaseNo("");
		header.setShipTo(this.createShipTo(objectFactory, po));
		header.setVendorInformation(this.createVendorInformation(objectFactory,
				po));
		header.setAttachments(this.createPOAttachments(objectFactory, po));

		return header;
	}

	public Summary createSummary(ObjectFactory objectFactory, PO po) {
		Summary summary = objectFactory.createSummary();
		summary.setAdditionalFields(this.createSummaryAdditionalFields(
				objectFactory, po));
		summary.setTotalAmount(po.getTotalAmtDue());
		summary.setTotalLineItems(po.getPoItems().size());

		return summary;
	}

	public List<ItemDetail> createItemDetails(ObjectFactory objectFactory, PO po) {
		List<ItemDetail> itemDetails = new ArrayList<ItemDetail>();
		Set<POItem> poItems = po.getPoItems();
		for (POItem poItem : poItems) {
			ItemDetail itemDetail = objectFactory.createItemDetail();
			itemDetail.setAdditionalFields(this.createItemAdditionalFields(
					objectFactory, po, poItem));
			itemDetail.setBuyerPartNum(poItem.getBuyerPartNum());
			itemDetail.setDeliverByDate(poItem.getDeliverByDate().toString());
			itemDetail.setItemStatus("NEW");
			itemDetail.setLineItemTotal(poItem.getUnitPrice().multiply(
					poItem.getOrderQty()));
			itemDetail.setLineNum(poItem.getLineNum().intValue());
			itemDetail.setMfrName(poItem.getMfrName());
			itemDetail.setOrderQty(poItem.getOrderQty());
			itemDetail.setSupplierPartNum(poItem.getSupplierPartNum());
			itemDetail.setSupplierPartNumExt(poItem.getSupplierPartNumExt());
			itemDetail.setUnitPrice(poItem.getUnitPrice());
			itemDetail.setUOM(poItem.getUOMID());
			itemDetail.setAttachments(this.createPOItemAttachments(
					objectFactory, po, poItem));

			itemDetails.add(itemDetail);
		}

		return itemDetails;
	}

	public AdditionalFields createItemAdditionalFields(
			ObjectFactory objectFactory, PO po, POItem poItem) {
		AdditionalFields additionalFields = objectFactory
				.createAdditionalFields();
		additionalFields.setAdditionalField1("");
		additionalFields.setAdditionalField2("");
		additionalFields.setAdditionalField3("");
		additionalFields.setAdditionalField4("");
		additionalFields.setAdditionalField5("");
		additionalFields.setAdditionalField6("");
		additionalFields.setAdditionalField7("");
		additionalFields.setAdditionalField8("");
		additionalFields.setAdditionalField9("");
		additionalFields.setAdditionalField10("");
		additionalFields.setAdditionalField11("");

		return additionalFields;
	}

	public AdditionalFields createHeaderAdditionalFields(
			ObjectFactory objectFactory, PO po) {
		AdditionalFields additionalFields = objectFactory
				.createAdditionalFields();
		additionalFields.setAdditionalField1("");
		additionalFields.setAdditionalField2("");
		additionalFields.setAdditionalField3("");
		additionalFields.setAdditionalField4("");
		additionalFields.setAdditionalField5("");
		additionalFields.setAdditionalField6("");
		additionalFields.setAdditionalField7("");
		additionalFields.setAdditionalField8("");
		additionalFields.setAdditionalField9("");
		additionalFields.setAdditionalField10("");
		additionalFields.setAdditionalField11("");

		return additionalFields;
	}

	public AdditionalFields createSummaryAdditionalFields(
			ObjectFactory objectFactory, PO po) {
		AdditionalFields additionalFields = objectFactory
				.createAdditionalFields();

		additionalFields.setAdditionalField1("");
		additionalFields.setAdditionalField2("");
		additionalFields.setAdditionalField3("");
		additionalFields.setAdditionalField4("");
		additionalFields.setAdditionalField5("");
		additionalFields.setAdditionalField6("");
		additionalFields.setAdditionalField7("");
		additionalFields.setAdditionalField8("");
		additionalFields.setAdditionalField9("");
		additionalFields.setAdditionalField10("");
		additionalFields.setAdditionalField11("");

		return additionalFields;
	}

	public ShipTo createShipTo(ObjectFactory objectFactory, PO po) {
		ShipTo shipTo = objectFactory.createShipTo();
		shipTo.setAddress1(po.getStatic_Ship_To().getAddress1());
		shipTo.setAddress2(po.getStatic_Ship_To().getAddress2());
		shipTo.setAddress3(po.getStatic_Ship_To().getAddress3());
		shipTo.setAddress4(po.getStatic_Ship_To().getAddress4());
		shipTo.setAddress5(po.getStatic_Ship_To().getAddress5());
		shipTo.setCity(po.getStatic_Ship_To().getCity());
		shipTo.setCompanyName1(po.getStatic_Ship_To().getCompanyName1());
		shipTo.setCompanyName2(po.getStatic_Ship_To().getCompanyName2());
		shipTo.setCompanyName3(po.getStatic_Ship_To().getCompanyName3());
		shipTo.setCountry(po.getStatic_Ship_To().getCountryCode());
		shipTo.setEmail(po.getStatic_Ship_To().getEmail());
		shipTo.setFax(po.getStatic_Ship_To().getFax());
		shipTo.setPostalCode(po.getStatic_Ship_To().getPostalCode());
		shipTo.setStateProvince(po.getStatic_Ship_To().getStateOrProvince());
		shipTo.setTel(po.getStatic_Ship_To().getTelephone());
		shipTo.setPlantCode("");

		return shipTo;
	}

	public BillTo createBillTo(ObjectFactory objectFactory, PO po) {
		BillTo billTo = objectFactory.createBillTo();
		billTo.setAddress1(po.getStatic_Bill_To().getAddress1());
		billTo.setAddress2(po.getStatic_Bill_To().getAddress2());
		billTo.setAddress3(po.getStatic_Bill_To().getAddress3());
		billTo.setAddress4(po.getStatic_Bill_To().getAddress4());
		billTo.setAddress5(po.getStatic_Bill_To().getAddress5());
		billTo.setCity(po.getStatic_Bill_To().getCity());
		billTo.setCompanyName1(po.getStatic_Bill_To().getCompanyName1());
		billTo.setCompanyName2(po.getStatic_Bill_To().getCompanyName2());
		billTo.setCompanyName3(po.getStatic_Bill_To().getCompanyName3());
		billTo.setContactName(po.getStatic_Bill_To().getContactName());
		billTo.setCountry(po.getStatic_Bill_To().getCountryCode());
		billTo.setEmail(po.getStatic_Bill_To().getEmail());
		billTo.setFax(po.getStatic_Bill_To().getFax());
		billTo.setPostalCode(po.getStatic_Bill_To().getPostalCode());
		billTo.setStateProvince(po.getStatic_Bill_To().getStateOrProvince());
		billTo.setTel(po.getStatic_Bill_To().getTelephone());

		return billTo;
	}

	public BuyerInformation createBuyerInformation(ObjectFactory objectFactory,
			PO po) {
		BuyerInformation buyerInformation = objectFactory
				.createBuyerInformation();
		buyerInformation.setAddress(this.createBuyerAddress(objectFactory, po));
		buyerInformation.setBusinessRegistrationNo("");
		buyerInformation.setBuyerCode(po.getBorg().getBuyerCode() == null ? ""
				: po.getBorg().getBuyerCode());
		buyerInformation
				.setBuyerCompanyCode(this.applicationProperties
						.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_HubID));
		buyerInformation.setBuyerCompanyName(po.getBorg().getBorgName());
		buyerInformation.setBuyerContactID("");
		buyerInformation.setBuyerContactName(po.getBorg().getBillTo()
				.getContact().getContactName());
		buyerInformation.setBuyerMailBoxID(po.getBorg().getBillTo()
				.getContact().getContactLocation());
		buyerInformation.setBuyerName(po.getBorg().getBorgName());
		buyerInformation
				.setBuyerPortalID(this.applicationProperties
						.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_BuyerPortalID));
		buyerInformation.setRequestorEmail(po.getPurchaserEmail());
		buyerInformation.setRequestorFax(po.getPurchaserFax());
		buyerInformation.setRequestorName(po.getPurchaserContactName());
		buyerInformation.setRequestorTel(po.getPurchaserTelephone());
		buyerInformation.setSigningAuthority(this.createSigningAuthority(
				objectFactory, po));
		buyerInformation.setTaxRegistrationNo("");

		return buyerInformation;
	}

	public Address createBuyerAddress(ObjectFactory objectFactory, PO po) {
		Address address = objectFactory.createAddress();
		address.setAddress1(po.getBorg().getBillTo().getAddress().getAddress1());
		address.setAddress2(po.getBorg().getBillTo().getAddress().getAddress2());
		address.setAddress3(po.getBorg().getBillTo().getAddress().getAddress3());
		address.setAddress4(po.getBorg().getBillTo().getAddress().getAddress4());
		address.setAddress5(po.getBorg().getBillTo().getAddress().getAddress5());
		address.setCity(po.getBorg().getBillTo().getAddress().getCity());
		address.setCountry(po.getBorg().getBillTo().getAddress()
				.getCountryCode());
		address.setEmail(po.getBorg().getBillTo().getContact().getEmail());
		address.setFax(po.getBorg().getBillTo().getContact().getFax());
		address.setPostalCode(po.getBorg().getBillTo().getAddress()
				.getPostalCode());
		address.setStateProvince(po.getBorg().getBillTo().getAddress()
				.getStateOrProvince());
		address.setTel(po.getBorg().getBillTo().getContact().getTelephone());
		return address;
	}

	public SigningAuthority createSigningAuthority(ObjectFactory objectFactory,
			PO po) {
		SigningAuthority signingAuthority = objectFactory
				.createSigningAuthority();
		signingAuthority.setActionDate("");
		signingAuthority.setActionTime("");
		signingAuthority.setDepartment("");
		signingAuthority.setDesignation("");
		signingAuthority.setUserID("");
		signingAuthority.setUserName("");

		return signingAuthority;
	}

	public OrderInformation createOrderInformation(ObjectFactory objectFactory,
			PO po) {
		OrderInformation orderInformation = objectFactory
				.createOrderInformation();
		orderInformation.setContractRef("");
		orderInformation.setOrderDate(po.getOrderedOnDate().toString());
		orderInformation
				.setOrderType(this.applicationProperties
						.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_OrderType));
		orderInformation.setPOID(po.getPOID().intValue());
		orderInformation.setPONO(po.getPONum());
		if (po.getPOStatusID().intValue() == IConstants.POStatus.APPROVED
				.intValue()) {
			orderInformation
					.setPOStatus(this.applicationProperties
							.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_POStatus_NEW));
		} else if (po.getPOStatusID().intValue() == IConstants.POStatus.CANCELLED_BY_BUYER
				.intValue()) {
			orderInformation
					.setPOStatus(this.applicationProperties
							.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_POStatus_CANCEL));
		} else {
			String message = String
					.format("Exception occured while generating PO XML for PO: %d. The status of PO is not either Approved or Cancelled by buyer",
							po.getPOID());
			throw new ApplicationException(message);
		}

		orderInformation.setPurposeCode("");

		return orderInformation;
	}

	public PaymentAndDelivery createPaymentAndDelivery(
			ObjectFactory objectFactory, PO po) {
		PaymentAndDelivery paymentAndDelivery = objectFactory
				.createPaymentAndDelivery();
		paymentAndDelivery.setCurrency1(po.getCurrencyCode());
		paymentAndDelivery.setExchangeRateDetail(this.createExchangeRateDetail(
				objectFactory, po));
		paymentAndDelivery.setPaymentTermNotes("");
		paymentAndDelivery.setTermsOfPayment(po.getStatic_Payment()
				.getPayment_Term_Buyer_Desc());
		paymentAndDelivery.setTermsOfDelivery(po.getStatic_Supplier()
				.getShipMethod().getBuyerDesc());
		paymentAndDelivery.setTermsOfDeliveryNotes("");

		return paymentAndDelivery;
	}

	public ExchangeRateDetail createExchangeRateDetail(
			ObjectFactory objectFactory, PO po) {
		ExchangeRateDetail exchangeRateDetail = objectFactory
				.createExchangeRateDetail();
		exchangeRateDetail.setExchangeRate("");
		exchangeRateDetail.setTargetCurrency("");

		return exchangeRateDetail;
	}

	public VendorInformation createVendorInformation(
			ObjectFactory objectFactory, PO po) {
		VendorInformation vendorInformation = objectFactory
				.createVendorInformation();
		vendorInformation.setAddress(this
				.createVendorAddress(objectFactory, po));
		vendorInformation.setBusinessRegistrationNo("");
		vendorInformation.setTaxRegistrationNo("");
		vendorInformation.setVendorCode("");
		vendorInformation.setVendorCompanyCode(po.getStatic_Supplier()
				.getSupplier().getGTPCoyCode());
		vendorInformation.setVendorCompanyName(po.getStatic_Supplier()
				.getName());
		vendorInformation.setVendorContactName(po.getStatic_Supplier()
				.getContactName());
		vendorInformation
				.setVendorPortalID(this.applicationProperties
						.getProperty(IConstants.ApplicationProperties.NHG_PO_XML_VendorPortalID));

		return vendorInformation;
	}

	public Address createVendorAddress(ObjectFactory objectFactory, PO po) {
		Address address = objectFactory.createAddress();
		address.setAddress1(po.getStatic_Supplier().getAddress1());
		address.setAddress2(po.getStatic_Supplier().getAddress2());
		address.setAddress3(po.getStatic_Supplier().getAddress3());
		address.setAddress4(po.getStatic_Supplier().getAddress4());
		address.setAddress5(po.getStatic_Supplier().getAddress5());
		address.setCity(po.getStatic_Supplier().getCity());
		address.setCountry(po.getStatic_Supplier().getCountryCode());
		address.setEmail(po.getStatic_Supplier().getEmail());
		address.setFax(po.getStatic_Supplier().getFax());
		address.setPostalCode(po.getStatic_Supplier().getPostalCode());
		address.setStateProvince(po.getStatic_Supplier().getStateOrProvince());
		address.setTel(po.getStatic_Supplier().getTelephone());

		return address;
	}

	public List<Attachment> createPOAttachment(ObjectFactory objectFactory,
			PO po) {
		List<Attachment> attachments = new ArrayList<Attachment>();
		Set<POAttachment> poAttachments = po.getPoAttachments();
		for (POAttachment poAttachment : poAttachments) {
			if (poAttachment.getSendtoSupplier()) {
				Attachment attachment = objectFactory.createAttachment();
				attachment.setAttachmentName(poAttachment.getAttachmentName());
				attachment
						.setAttachmentNameUnique(this.applicationProperties
								.getProperty(IConstants.ApplicationProperties.NHG_PO_Attachments_location)
								+ poAttachment.getAttachmentNameUnique());
				attachment.setPurpose(poAttachment.getPurpose());
				attachments.add(attachment);
			}
		}

		return attachments;
	}

	public List<Attachment> createPOItemAttachment(ObjectFactory objectFactory,
			PO po, POItem poItem) {
		List<Attachment> attachments = new ArrayList<Attachment>();
		Set<POItemAttachment> poItemAttachments = poItem.getPoItemAttachments();
		for (POItemAttachment poItemAttachment : poItemAttachments) {
			if (poItemAttachment.getSendtoSupplier()) {
				Attachment attachment = objectFactory.createAttachment();
				attachment.setAttachmentName(poItemAttachment
						.getAttachmentName());
				attachment.setAttachmentNameUnique(poItemAttachment
						.getAttachmentNameUnique());
				attachment.setPurpose(poItemAttachment.getPurpose());
				attachments.add(attachment);
			}
		}

		return attachments;
	}

	public Attachments createPOAttachments(ObjectFactory objectFactory, PO po) {
		Attachments attachments = objectFactory.createAttachments();
		attachments.getAttachment().addAll(
				this.createPOAttachment(objectFactory, po));

		return attachments;
	}

	public Attachments createPOItemAttachments(ObjectFactory objectFactory,
			PO po, POItem poItem) {
		Attachments attachments = objectFactory.createAttachments();
		attachments.getAttachment().addAll(
				this.createPOItemAttachment(objectFactory, po, poItem));

		return attachments;
	}

	public void generatePOXMLsForCanceled() {
		List<PO> pos = this.podao.getAllCanceledPOReadyToSend();
		for (PO po : pos) {
			this.generatePOXML(po);
		}
	}

}
